/*java jdk 1.7+
# -*- coding: utf-8 -
'''
Created on 2020年05月10日
@author: zxyong 13738196011
'''*/

package com.zxy.db_Self;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;

import com.zxy.common.Com_Fun;
import com.zxy.common.Com_Para;

public class ConnectionPool_Self
{
	public String							jdbcDriver				= "";
	private String							dbUrl					= "";
	private String							dbUsername				= "";
	private String							dbPassword				= "";
	private int								initialConnections		= 5;
	private int								incrementalConnections	= Com_Para.incrementalConnections;
	private int								maxConnections			= Com_Para.maxConnections;
	private Vector<PooledConnection_Self>	connections				= null;

	public ConnectionPool_Self(String jdbcDriver, String dbUrl, String dbUsername, String dbPassword)
	{
		if(jdbcDriver.equals("org.sqlite.JDBC"))
			this.initialConnections = 2;
		this.jdbcDriver = jdbcDriver;
		this.dbUrl = dbUrl;
		this.dbUsername = dbUsername;
		this.dbPassword = dbPassword;
		// createPool();
	}

	public synchronized void createPool()
	{
		if(!java.util.Objects.equals(this.connections,null))
		{
			return;
		}
		try
		{
			Driver driver = (Driver) Class.forName(this.jdbcDriver).newInstance();
			DriverManager.registerDriver(driver);
			this.connections = new Vector<PooledConnection_Self>();
			createConnections(this.initialConnections);
			System.out.println("my_self:db_url==>" + this.dbUrl);
			System.out.println("myself db create pool");
		}
		catch(Exception e)
		{
			Com_Fun.debug_in(e,this.toString());
		}
	}

	private void createConnections(int numConnections)
	{
		for(int x = 0;x < numConnections;x++)
		{
			if((this.maxConnections > 0)
					&& (this.connections.size() >= this.maxConnections))
			{
				System.out.println("myself db 连接数己经达到最大");
				break;
			}
			try
			{
				this.connections.addElement(new PooledConnection_Self(newConnection()));
			}
			catch(Exception e)
			{
				Com_Fun.debug_in(e,this.toString(),"");
			}
		}
	}

	private Connection newConnection() throws SQLException
	{
		// 创建一个数据库连接
		Connection conn = DriverManager.getConnection(this.dbUrl,this.dbUsername,this.dbPassword);
//		try
//		{
//			com.zxy.adminlog.UsAdmin_Log uL = new com.zxy.adminlog.UsAdmin_Log(Com_Para.ApplicationPath,new StringBuilder("+1 总数:"
//				+ connections.size() + " DT:"
//				+ Com_Fun.DateTimeStr("yyyy-MM-dd HH:mm:ss")+"=>"+conn.toString()),"create_dbmyself");
//		
//			uL.WriteLog();
//		}
//		catch(IOException e1)
//		{
//		}
		// 如果这是第一次创建数据库连接，即检查数据库，获得此数据库允许支持的最大客户连接数目
		if(connections.size() == 0)
		{
			DatabaseMetaData metaData = conn.getMetaData();
			int driverMaxConnections = metaData.getMaxConnections();
			// 数据库返回的 driverMaxConnections 若为 0 ，表示此数据库没有最大
			// 连接限制，或数据库的最大连接限制不知道
			// driverMaxConnections 为返回的一个整数，表示此数据库允许客户连接的数目
			// 如果连接池中设置的最大连接数量大于数据库允许的连接数目 , 则置连接池的最大
			// 连接数目为数据库允许的最大数目
			if(driverMaxConnections > 0
					&& this.maxConnections > driverMaxConnections)
			{
				this.maxConnections = driverMaxConnections;
			}
		}
		// 返回创建的新的数据库连接
		return conn;
	}

	public synchronized PooledConnection_Self getConnection() throws SQLException
	{
		if(java.util.Objects.equals(this.connections,null))
		{
			return null;
		}
		PooledConnection_Self conn = getFreeConnection();
		while(java.util.Objects.equals(conn,null))
		{
			wait(150);
			conn = getFreeConnection();
		}
		return conn;
	}

	private PooledConnection_Self getFreeConnection() throws SQLException
	{
		PooledConnection_Self conn = findFreeConnection();
		if(java.util.Objects.equals(conn,null))
		{
			createConnections(this.incrementalConnections);
			conn = findFreeConnection();
			if(java.util.Objects.equals(conn,null))
			{
				return null;
			}
		}
		return conn;
	}

	private PooledConnection_Self findFreeConnection() throws SQLException
	{
		for(int i = 0;i < this.connections.size();i++)
		{
			PooledConnection_Self pc = (PooledConnection_Self) this.connections.elementAt(i);
			if(!pc.isBusy())
			{
				Connection conn = pc.getConnection();
				pc.setBusy(true);
				if(!isValid(conn))
				{
					try
					{
						conn = newConnection();
						pc.setConnection(conn);
					}
					catch(Exception e)
					{
						this.connections.remove(i--);
						Com_Fun.debug_in(e,this.toString());
						continue;
					}
				}
				return pc;
			}
		}
		return null;
	}

	private boolean isValid(Connection conn)
	{
		try
		{
			if(this.jdbcDriver.equals("org.sqlite.JDBC"))
				return !conn.isClosed();
			else if(this.jdbcDriver.indexOf("oracle") != -1)
				return conn.isValid(1000);
			else
				return !conn.isClosed();
		}
		catch(Exception e)
		{
			Com_Fun.debug_in(e,this.toString());
			return false;
		}
	}

	public void returnConnection(Connection conn)
	{
		if(java.util.Objects.equals(this.connections,null))
		{
			System.out.println("myself db  连接池不存在，无法返回此连接到连接池中 !");
			return;
		}
		PooledConnection_Self pConn = null;
		Enumeration<PooledConnection_Self> enumerate = this.connections.elements();
		while(enumerate.hasMoreElements())
		{
			pConn = (PooledConnection_Self) enumerate.nextElement();
			if(conn.equals(pConn.getConnection()))
			{
				pConn.setBusy(false);
				break;
			}
		}
	}

	public synchronized void refreshConnections()
	{
		if(java.util.Objects.equals(this.connections,null))
		{
			System.out.println("myself db  连接池不存在，无法刷新 !");
			return;
		}

		PooledConnection_Self pConn = null;
		Enumeration<PooledConnection_Self> enumerate = this.connections.elements();
		while(enumerate.hasMoreElements())
		{
			pConn = (PooledConnection_Self) enumerate.nextElement();

			if(!pConn.isBusy())
			{
				ResultSet rs = null;
				try
				{
					rs = pConn.connection.createStatement().executeQuery("select 1 from dual");
				}
				catch(SQLException e)
				{
					Com_Fun.debug_in(e,this.toString());
				}
				finally
				{
					try
					{
						if(!java.util.Objects.equals(rs,null))
						{
							rs.getStatement().close();
							rs.close();
						}
					}
					catch(SQLException e)
					{
					}

					pConn.returnConnection();
				}
			}
		}
	}

	// 超时连接。
	public synchronized void closeConnectionPoolTimeOut() throws SQLException
	{
		PooledConnection_Self pConn = null;
		if(!java.util.Objects.equals(connections,null))// &&
														// !this.jdbcDriver.equals("org.sqlite.JDBC"))
		{
			int iIndex = 0;
			Enumeration<PooledConnection_Self> enumerate = connections.elements();
			while(enumerate.hasMoreElements())
			{
				pConn = (PooledConnection_Self) enumerate.nextElement();
				if(pConn.updtime + 180000 < new Date().getTime())// 300000
				{
					isValid(pConn.getConnection());
					if(iIndex <= initialConnections && !pConn.isBusy())
					{
						ResultSet rs = null;
						com.zxy.adminlog.UsAdmin_Log uL = null;
						String connLog = "";
						try
						{
//							connLog = pConn.connection.toString();
//							String strLog = connLog+"=>"+Com_Fun.DateTimeStr("yyyy-MM-dd HH:mm:ss");							
//							String strFL = connLog.substring(connLog.lastIndexOf(".")+1,connLog.length());
//							uL = new com.zxy.adminlog.UsAdmin_Log(Com_Para.ApplicationPath,new StringBuilder(strLog),"dbmy_"+strFL);
							
							if(pConn.connection.getClass().toString().indexOf("oracle") != -1)
								rs = pConn.executeQuery(new StringBuilder("select sysdate from dual"));
							else
								rs = pConn.executeQuery(new StringBuilder("select 1 from dual"));
							
//							try
//							{
//								uL.WriteLog();
//							}
//							catch(IOException e1)
//							{
//							}
						}
						catch(Exception e)
						{
//							System.out.println(connLog+"=>dbmy==>"+Com_Fun.DateTimeStr("yyyy-MM-dd HH:mm:ss"));
							//异常则关闭连接
							closeConnection(pConn.getConnection());
							connections.removeElement(pConn);
							Com_Fun.debug_in(e,this.toString());
						}
						finally
						{
							try
							{
								if(!java.util.Objects.equals(rs,null))
								{
									rs.getStatement().close();
									rs.close();
								}
							}
							catch(SQLException e)
							{
							}
							pConn.returnConnection();
						}
					}
					// 超过initialConnections个连接关闭
					else if(iIndex > initialConnections && !pConn.isBusy())
					{
						closeConnection(pConn.getConnection());
						connections.removeElement(pConn);
						System.out.println("删除mySelf连接,当前总连接数:"
								+ this.connections.size());
					}
				}
				iIndex++;
			}
		}
	}

	public synchronized void closeConnectionPool() throws SQLException
	{
		if(java.util.Objects.equals(this.connections,null))
		{
			System.out.println("myself db  连接池不存在，无法关闭 !");
			return;
		}

		PooledConnection_Self pConn = null;
		Enumeration<PooledConnection_Self> enumerate = this.connections.elements();
		while(enumerate.hasMoreElements())
		{
			pConn = (PooledConnection_Self) enumerate.nextElement();
			if(pConn.isBusy())
			{
				wait(5000);
			}
			closeConnection(pConn.getConnection());
			this.connections.removeElement(pConn);
			System.out.println("删除mySelf连接,当前总连接数:" + this.connections.size());
		}
		this.connections = null;
	}

	private void closeConnection(Connection conn)
	{
		try
		{
//			System.out.println("the self 关闭数据库进行时"
//					+ Com_Fun.DateTimeStr("yyyy-MM-dd HH:mm:ss"));
			conn.close();
//			com.zxy.adminlog.UsAdmin_Log uL = new com.zxy.adminlog.UsAdmin_Log(Com_Para.ApplicationPath,new StringBuilder("-1 总数:"
//					+ connections.size() + " DT:"
//					+ Com_Fun.DateTimeStr("yyyy-MM-dd HH:mM:ss")),"close_dbself");
//			try
//			{
//				uL.WriteLog();
//			}
//			catch(IOException e1)
//			{
//			}
		}
		catch(SQLException e)
		{
			Com_Fun.debug_in(e,this.toString());
		}
	}

	private void wait(int mSeconds)
	{
		try
		{
			Thread.sleep(mSeconds);
		}
		catch(Exception e)
		{
		}
	}

	public int getInitialConnections()
	{
		return this.initialConnections;
	}

	public void setInitialConnections(int initialConnections)
	{
		this.initialConnections = initialConnections;
	}

	public int getIncrementalConnections()
	{
		return this.incrementalConnections;
	}

	public void setIncrementalConnections(int incrementalConnections)
	{
		this.incrementalConnections = incrementalConnections;
	}

	public int getMaxConnections()
	{
		return this.maxConnections;
	}

	public void setMaxConnections(int maxConnections)
	{
		this.maxConnections = maxConnections;
	}
}